#include "stdafx.h"

#include "Engine.h"


 // PARSING OPENING TEXT FILE
///////////////////////////////////////////////////////////////////////////////////////////////////

#define OPN_MAX_MOVES 200
#define OPN_MAX_ITEMS 2
#define OPN_MAX_LINE 1000

BitBoardMove openingMoves[OPN_MAX_MOVES][OPN_MAX_ITEMS];
int openingMoveCount;

char szOpenLine[OPN_MAX_LINE];
int iOpnLineIndex;

bool IsWhitespace(char c)
{
	switch(c)
	{
	case ' ':
	case '\t':
	case '\n':
	case '\r':
		return true;
	default:
		return false;
	}
}

bool ReadLine(std::ifstream *ifs)
{
	if(ifs->eof())
		return false;
	ifs->getline(szOpenLine, OPN_MAX_LINE);
	iOpnLineIndex = 0;

	return true;
}

bool GetToken(char* pChar)
{
	*pChar = NULL;

	while(true)
	{
		if(szOpenLine[iOpnLineIndex] == 0)
			return false;
		
		if(szOpenLine[iOpnLineIndex] == ';') // comment
			return false;
		
		if(IsWhitespace(szOpenLine[iOpnLineIndex]))
		{
			iOpnLineIndex++;
			continue;
		}

		*pChar = szOpenLine[iOpnLineIndex++];
		return true;
	}
}

void UngetToken()
{
	iOpnLineIndex--;
}

int ConvertLetterCoordinate(char letter)
{
	switch(letter)
	{
	case 'a':
		return 0;
	case 'b':
		return 1;
	case 'c':
		return 2;
	case 'd':
		return 3;
	case 'e':
		return 4;
	case 'f':
		return 5;
	case 'g':
		return 6;
	case 'h':
		return 7;
	default:
		assert(false);
		return -1;
	}
}

bool ParseCoordinate(uint32* bitboard)
{
	char letter;
	if(GetToken(&letter) == false)
	{
		assert(false);
		return false;
	}
	char number[2];
	if(GetToken(&number[0]) == false)
	{
		assert(false);
		return false;
	}

	int col = ConvertLetterCoordinate(letter);
	if(col < 0)
		return false;

	number[1] = 0;
	int row = atoi(number);
	if(row < 1 || row > 8)
	{
		assert(false);
		return false;
	}
	row--;

	int shift = row * 4;
	if(row % 2 == 0)
		shift += col / 2;
	else
		shift += (col - 1) / 2;

	*bitboard = 1 << shift;
	return true;
}


bool ParseMove(BitBoardMove* bb, bool bWhite)
{
	// clear
	memset(bb, 0, sizeof(BitBoardMove));

	// parse first coordinate
	uint32 bit;
	if(ParseCoordinate(&bit) == false)
		return false;
	if(bWhite)
		bb->wm |= bit;
	else
		bb->bm |= bit;

	// check move sign
	char c;
	if(GetToken(&c) == false)
	{
		assert(false);
		return false;
	}
	if(c != '-')
	{
		assert(false);
		return false;
	}

	// parse second coordinate
	if(ParseCoordinate(&bit) == false)
		return false;
	if(bWhite)
		bb->wm |= bit;
	else
		bb->bm |= bit;

	return true;
}


bool ParseMoveLine(int idx)
{
	// parse white move
	if(ParseMove(&openingMoves[idx][0], true) == false)
		return false;

	// parse black move
	if(ParseMove(&openingMoves[idx][1], false) == false)
		return false;
	return true;
}

bool ParseMoves(std::ifstream* ifs)
{
	// clearing
	openingMoveCount = 0;
	memset(openingMoves, 0, sizeof(BitBoardMove) * OPN_MAX_MOVES * OPN_MAX_ITEMS);

	while(ReadLine(ifs) == true)
	{
		char c;

		// comment or empty line
		if(GetToken(&c) == false)
			continue;

		UngetToken();

		if(ParseMoveLine(openingMoveCount) == false)
		{
			assert(false);
			return false;
		}

		openingMoveCount++;

	} // while

	return true;
}


bool ReadOpeningBook(std::string sFilename)
{
	std::ifstream ifs(sFilename);
	if(ifs.bad())
	{
		assert(false);
		return false;
	}

	bool bResult = ParseMoves(&ifs);

	ifs.close();

	return bResult;
}


void LogOpeningBook()
{
	int logId = LogInit("c:\\opening.log");

	for(int i = 0; i < openingMoveCount; i++)
	{
		LogMessage(logId, L"WHITE\n");
		LogBB(logId, openingMoves[i][0]);
		LogMessage(logId, L"BLACK\n");
		LogBB(logId, openingMoves[i][1]);
	}

	LogClose(logId);
}

 // CHANGING MOVES TO POSITIONS
////////////////////////////////////////////////////////////////////////////////////////////////

#define MAX_POSITIONS 100
#define MAX_OPN_MOVES_FOR_POS 100

BitBoard positions[MAX_POSITIONS];
int positionCount;
BitBoardMove positionMoves[MAX_POSITIONS][MAX_OPN_MOVES_FOR_POS];
int positionMovesLength[MAX_POSITIONS];


void ClearPositionArrays()
{
	memset(positions, 0, sizeof(BitBoard) * MAX_POSITIONS);
	memset(positionMoves, 0, sizeof(BitBoardMove) * MAX_POSITIONS * MAX_OPN_MOVES_FOR_POS);
	memset(positionMovesLength, 0, sizeof(int) * MAX_POSITIONS);
	positionCount = 0;
}

bool CompareBitBoards(BitBoard* bb1, BitBoard *bb2)
{
	if(bb1->wm == bb2->wm && bb1->wk == bb2->wk && bb1->bm == bb2->bm && bb1->bk == bb2->bk)
		return true;
	else
		return false;
}

void SetStartUpPosition(BitBoard* bb)
{
	memset(bb, 0, sizeof(BitBoard));
	
	// set white side
	for(int i = 0; i <= 11; i++)
		bb->wm |= 1 << i;

	// set black side
	for(int i = 31; i >= 31-11; i--)
		bb->bm |= 1 << i;
}

void CompleteStartUpPosition()
{
	// set startup position
	BitBoard bb;
	SetStartUpPosition(&bb);

	positions[positionCount++] = bb;	

	BitBoardMove lastMove;
	memcpy(&lastMove, &openingMoves[0][0], sizeof(BitBoardMove));

	int moveCount = 0;
	for(int i = 0; i < openingMoveCount; i++)
	{
		// copy the move
		memcpy(&positionMoves[0][moveCount], &lastMove, sizeof(BitBoardMove));
		moveCount++;

		// search for the next move
		while(i < openingMoveCount && CompareBitBoards(&lastMove, &openingMoves[i][0]) == true)
			i++;
		if(i >= openingMoveCount)
			break;

		memcpy(&lastMove, &openingMoves[i][0], sizeof(BitBoardMove));
	} // for

	positionMovesLength[0] = moveCount;
}

void CompletePosition(BitBoard* bb, BitBoardMove* bbPreviousMove, int moveIndex)
{
	// set the new position
	int positionIndex = positionCount;
	positions[positionCount++] = *bb;

	int moveCount = 0;
	for(int i = 0; i < openingMoveCount; i++)
	{
		if(CompareBitBoards(bbPreviousMove, &openingMoves[i][moveIndex]) == true)
		{
			// copy the move
			memcpy(&positionMoves[positionIndex][moveCount], &openingMoves[i][moveIndex+1], sizeof(BitBoardMove));
			moveCount++;
		}
	}

	positionMovesLength[positionIndex] = moveCount;
}

void CompleteMovesToPositions()
{
	// do initial clearing
	ClearPositionArrays();

	// complete startup position
	CompleteStartUpPosition();

	// complete following moves, just one move ahead, there is no definition for opening 
	// in Czech Checkers further
	for(int i = 0; i < positionMovesLength[0]; i++)
	{
		// prepare bitboard
		BitBoard bb;
		SetStartUpPosition(&bb);
		CzechMoveGen::ToggleMove(&bb, &positionMoves[0][i]);

		CompletePosition(&bb, &positionMoves[0][i], 0);
	}
}

void LogPositions()
{
	int logId = LogInit("c:\\positions.log");

	for(int i = 0; i < positionCount; i++)
	{
		LogMessage(logId, L"POZICE: \n");
		LogBB(logId, positions[i]);

		LogMessage(logId, L"TAHY:\n");

		for(int j = 0; j < positionMovesLength[i]; j++)
			LogBB(logId, positionMoves[i][j]);
	}

	LogClose(logId);
}

bool bOpeningLoaded = false;

bool InitOpeningBook(std::string sFilename)
{
	// for multithreaded access
	if(bOpeningLoaded)
		return true;

	bOpeningLoaded = false;

	if(ReadOpeningBook(sFilename) == false)
		return false;

	// LogOpeningBook();

	CompleteMovesToPositions();

	// LogPositions();

	bOpeningLoaded = true;

	return true;
}

bool OpeningLookup(BitBoard* bb, BitBoardMove* bbMove, bool bWhite)
{
	if(bOpeningLoaded == false)
		return false;

	for(int i = 0; i < positionCount; i++)
	{
		if(CompareBitBoards(bb, &positions[i]) == true)
		{
			int moveCount = positionMovesLength[i];

			srand((unsigned int)time(NULL));
			int moveIndex = rand() % moveCount;
			memcpy(bbMove, &positionMoves[i][moveIndex], sizeof(BitBoardMove));

			if( (bWhite && bbMove->wm) || ( !bWhite && bbMove->bm) )
				return true;
			else
				return false;
		}
	}

	return false;
}

int GetOpeningMoveCount()
{
	return openingMoveCount;
}

BitBoardMove* GetOpeningMoveByIndex(int index, int& maxLen)
{
	maxLen = OPN_MAX_ITEMS;
	assert(index < openingMoveCount);
	return &openingMoves[index][0];
}

